import wx
import sys

import  wx.lib.mixins.listctrl  as  listmix
from pyeasyaccept.gui.CommandsDictionary import CommandsDictionary

class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
    """
    This class represents a ListCtrl
    @author: Mikaela Maia
    """
    def __init__(self,parent,ID=-1,pos=wx.DefaultPosition,size=wx.DefaultSize,style=0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)

class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin):
    """
    This class represents a textCtrl with auto complete function
    @author: Mikaela maia
    @author: Danilo Torres
    """

    def __init__ ( self, parent, cmdsDictionary, style=wx.TE_MULTILINE,choices = None, showHead=False, dropDownClick=True,colFetch=-1, colSearch=0, hideOnNoMatch=True,selectCallback=None, entryCallback=None,matchFunction=None,**therest) :
        if therest.has_key('style'):
            therest['style']=wx.TE_PROCESS_ENTER | therest['style']
        else:
            therest['style']=wx.TE_PROCESS_ENTER

        wx.TextCtrl.__init__(self,parent,-1,size=(300,20),style=therest['style'])

        self._hideOnNoMatch = hideOnNoMatch
        self._selectCallback = selectCallback
        self._matchFunction = matchFunction
        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y )

        #sort variable needed by listmix
        self.itemDataMap = dict()
        
        #widgets
        self.dropdown = wx.PopupWindow( self )

        #Control the style
        flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING
        if not (showHead) :
            flags = flags | wx.LC_NO_HEADER

        #Create the list and bind the events
        self.dropdownlistbox = myListCtrl( self.dropdown, style=flags, pos=wx.Point( 0, 0) )
        gp = self
        while ( gp != None ) :
           gp = gp.GetParent()
           
        self.SetAutoLayout(True)
        self.Bind( wx.EVT_TEXT , self.onEnteredText, self )
        self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self )
        self.Bind(wx.EVT_KILL_FOCUS, self.onKillFocus, self)
        self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK ,self.onListClick)
        self.dic = cmdsDictionary
        
    def getCmdsDictionary(self):
        return self.dic
    
    def setCmdsDictionary(self, dic):
        self.dic = dic
        
    def onKillFocus(self, event):
        """
        This method sets not to show drop down .
        """
        if self.dropdown.IsShown() :
             self._showDropDown(False)


    def onListClick(self, evt):
        """
        This method sets the value that was click to the text ctrl
        """ 
        self._setValueFromSelected()

    def onEnteredText(self, event):
        """
        This method shows drop down on entered text in the text ctrl.
        """

        text = self.GetTextToAutoComplete()
        
        if self._entryCallback:
            self._entryCallback()

        if not text:
            # control is empty; hide dropdown if shown:
            if self.dropdown.IsShown():
                self._showDropDown(False)
            event.Skip()
            return
        found = False
        choices = self._choices
        
        for numCh, choice in enumerate(choices):
            if self._matchFunction and self._matchFunction(text,
choice):
                found = True
            elif choice.lower().startswith(text.lower()) :
                found = True
            if found:
                self._showDropDown(True)
                item = self.dropdownlistbox.GetItem(numCh)
                toSel = item.GetId()
                self.dropdownlistbox.Select(toSel)
                break

        if not found:
                self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(),False)
                if self._hideOnNoMatch:
                    self._showDropDown(False)

        self._listItemVisible()

        event.Skip ()

    def onKeyDown ( self, event ) :
        """ Do some work when the user press on the keys:
            up and down: move the cursor
            left and right: move the search
        """
        skip = True

        sel = self.dropdownlistbox.GetFirstSelected()
        visible = self.dropdown.IsShown()

        KC = event.GetKeyCode()
        if KC == wx.WXK_DOWN :
            if sel < (self.dropdownlistbox.GetItemCount () - 1) :
                self.dropdownlistbox.Select ( sel+1 )
                self._listItemVisible()
            self._showDropDown ()
            skip = False
        elif KC == wx.WXK_UP :
            if sel > 0 :
                self.dropdownlistbox.Select ( sel - 1 )
                self._listItemVisible()
            self._showDropDown ()
            skip = False
        if visible :
            if event.GetKeyCode() == wx.WXK_RETURN :
                self._setValueFromSelected()
                skip = False
            if event.GetKeyCode() == wx.WXK_ESCAPE :
                self._showDropDown( False )
                skip = False
        if skip :
            event.Skip()

    def onListItemSelected (self, event):
        """
        This method sets the value from select on list item to the text ctrl 
        """
        self._setValueFromSelected()
        event.Skip()

    def onClickToggleDown(self, event):
        self._lastinsertionpoint = self.GetInsertionPoint()
        event.Skip ()

    def onClickToggleUp ( self, event ) :
        if ( self.GetInsertionPoint() == self._lastinsertionpoint ) :
            self._showDropDown ( not self.dropdown.IsShown() )
        event.Skip ()


    # -- Interfaces methods



    def SetChoices(self, choices):
        """
        This method sets the choices available in the popup wx.ListBox
        """

        #Sets the choices available in the popup wx.ListBox.
        #The items will be sorted case insensitively.
        self._choices = choices
        flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING| wx.LC_NO_HEADER
        self._updateDataList(self._choices)
        self.dropdownlistbox.InsertColumn(0, "")
        for num, colVal in enumerate(self._choices):
            index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
            self.dropdownlistbox.SetStringItem(index, 0, colVal)
            self.dropdownlistbox.SetItemData(index, num)

        self._setListSize()

        # there is only one choice for both search and fetch if setting a single column:
        self._colSearch = 0
        self._colFetch = -1
        
    def GetTextToAutoComplete(self): 
        """
        This method return the text to be auto completed
        @return:  text to be auto completed"""
        text = self.GetValue().lower()
        partition = text.rpartition(" ")
        if partition[2]!= "":
                text = partition[2]            
    
        return text
    
    def SetSelectCallback(self, cb=None):
        self._selectCallback = cb

    def SetEntryCallback(self, cb=None):
        self._entryCallback = cb
        
    def SetResetListCallback(self, cb=None):
        self._resetChoicesCallback = cb
        
    def autoComplete(self, arguments):        
        self.SetInsertionPointEnd ()
        self._showDropDown ( False )
        self.lineLengthBefore = self.GetLineLength(0)
        for arg in arguments:
            self.AppendText(" "+ arg)
        self.lineLengthAfter = self.GetLineLength(0)
        self.SetFocus()
        self.SetSelection(self.lineLengthBefore+1, self.lineLengthAfter)
        
    #-- Internal methods
    def _setValueFromSelected( self) :
         '''
         Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
         Will do nothing if no item is selected in the wx.ListCtrl.
         '''
         sel = self.dropdownlistbox.GetFirstSelected()
         if sel > -1:
            if self._colFetch != -1: col = self._colFetch
            else: col = self._colSearch
            
            itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
            
            if self._selectCallback:
                dd = self.dropdownlistbox
                values = [dd.GetItem(sel, x).GetText()
                    for x in xrange(dd.GetColumnCount())]
                self._selectCallback( values )

            text = self.GetTextToAutoComplete()
            index = itemtext.index(text)+len(text)
            self.AppendText (itemtext[index:])
            self.SetInsertionPointEnd ()
            self._showDropDown ( False )
            
            arguments = self.dic.getMethodArguments(itemtext)
            self.autoComplete(arguments)


    def _showDropDown ( self, show = True ) :
        '''
        Either display the drop down list (show = True) or hide it
(show = False).
        '''

        if show :
            size = self.dropdown.GetSize()
            width, height = self . GetSizeTuple()
            x, y = self . ClientToScreenXY ( wx.CentreX+6, wx.CentreY+6
)
            if size.GetWidth() != width :
                size.SetWidth(width)
                self.dropdown.SetSize(size)

                self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
            if (y + size.GetHeight()) < self._screenheight :
                self.dropdown . SetPosition ( wx.Point(x, y) )
            else:
                self.dropdown . SetPosition ( wx.Point(x, y - height -
size.GetHeight()) )
        else:
            if self._resetChoicesCallback:
                self._resetChoicesCallback()
        self.dropdown.Show ( show )


        
    def _listItemVisible( self ) :
        '''
        Moves the selected item to the top of the list ensuring it is
always visible.
        '''
        toSel =  self.dropdownlistbox.GetFirstSelected ()
        if toSel == -1: return
        self.dropdownlistbox.EnsureVisible( toSel )

    def _updateDataList(self, choices):
        """
        This method updates the data from the list box
        """
        #delete, if need, all the previous data
        if self.dropdownlistbox.GetColumnCount() != 0:
            self.dropdownlistbox.DeleteAllColumns()
            self.dropdownlistbox.DeleteAllItems()

        #and update the dict
        if choices:
            for numVal, data in enumerate(choices):
                self.itemDataMap[numVal] = data
        else:
            numVal = 0
        self.SetColumnCount(numVal)

    def _setListSize(self):
        """
        This method sets the list size
        """
        choices = self._choices
        longest = 0
        for choice in choices :
            longest = max(len(choice), longest)

        #longest += 3
        itemcount = min( len( choices ) , 7 ) + .5
        charheight = self.dropdownlistbox.GetCharHeight()
        charwidth = self.dropdownlistbox.GetCharWidth()
        self.popupsize = wx.Size( charwidth*longest,
charheight*itemcount )
        self.dropdownlistbox.SetSize ( self.popupsize )
        self.dropdown.SetClientSize( self.popupsize )

